package org.wheel.plugins.toolkit.util.sql;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Function;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.select.*;
import org.wheel.module.core.enm.NormalExceptionEnum;
import org.wheel.module.core.exception.NormalException;
import org.wheel.plugins.toolkit.sql.core.GlobalConfig;
import org.wheel.plugins.toolkit.sql.page.PaginationStrategy;
import org.wheel.plugins.toolkit.util.sql.expander.RowReverseVisitor;
import org.wheel.plugins.toolkit.util.sql.request.ParamSort;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;

import static org.wheel.module.core.util.ClassUtil.isBasicType;

public class SQLHelper {

    // 直接使用 LogFactory 创建日志对象，Hutool 会自动检测项目中的日志框架（优先级：Slf4j > Log4j > Logback > JDK Logging）
    private static final Log log = LogFactory.get();

    // #{} 占位符匹配规则
    private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("#\\{([\\w\\d_]+)}");

    /**
     * 用正则 \b别名\.列\b 抽“别名.列”，
     */
//    private static final Pattern ALIAS_DOT_PROP =
//            Pattern.compile(
//                    // 1. 列名（可选表别名）
//                    "\\b(?:\\w+\\.)?([\\w_]+)" +
//                            // 2. as / AS / aS / As + 可选空格 + 可选引号
//                            "\\s+[aA][sS]\\s+[\"']?" +
//                            // 3. 对象.属性（分组）
//                            "(\\w+)\\.(\\w+)" +
//                            // 4. 可选引号 + 单词边界
//                            "[\"']?\\b",
//                    Pattern.CASE_INSENSITIVE
//            );
    private static final Pattern ALIAS_DOT_PROP =
            Pattern.compile(
                    // 1. 列名（可选表别名）
                    "\\b(?:\\w+\\.)?([\\w_]+)" +
                            // 2. as / AS / aS / As + 可选空格 + 引号（可选）
                            "\\s+[aA][sS]\\s*\"?" +
                            // 3. 对象.属性（分组）
                            "(\\w+)\\.(\\w+)" +
                            // 4. 引号（可选）+ 单词边界
                            "\"?\\b",
                    Pattern.CASE_INSENSITIVE
            );


    /**
     * SQL解析，将占位符替换为 :paramName，并返回SQL语句，参数直接用原有Map，即可用NamedParameterJdbcTemplate执行得到结果。
     * 因为参数值有可能是集合，所以采用 :paramName 的方式进行参数绑定。
     * @param input SQL语句，例如：select * from table where id = #{id} and name = #{name}
     * @param parameters 参数列表 map存储，key为参数名，value为参数值。格式为：{id=1, name=张三}
     * @return Pair对象，key为SQL语句，SQL参数格式为“:%s”，例如：select * from table where id = :id and name = :name。value为参数列表。
     */
    public static String resolve(final String input, Map<String, Object> parameters) {
        Matcher matcher = PLACEHOLDER_PATTERN.matcher(input);
        StringBuilder result = new StringBuilder();

        while (matcher.find()) {
//            String placeholder = matcher.group(0);
            String key = matcher.group(1);

            String replacement = ":"+key;

            matcher.appendReplacement(result, Matcher.quoteReplacement(replacement));
        }

        matcher.appendTail(result);
        return result.toString();
    }

    /**
     * 将参数平铺成一个数组，发布jdbc的参数设置，
     * 例如：select * from table where id = ? and name = ?
     * 配合jdbcTemplate执行即可得到数据结果。
     * @param parameters 参数列表，必须是有序的，且key为参数名，value为参数值。<br>
     * 参数{@code parameters} 传入的格式 {@code LinkedHashMap<String, Object>} ，具体样例格式为：<pre>{@code
     * {
     *   "1": {"fieldName": "value"},
     *   "2": { "key_1": [1, 2, 3]},
     *   "3": { "key_2: "xxxxxxxx"}
     * }}</pre>
     * 或者正常的Map格式：<pre>{@code
     * {
     *   "fieldName": "value",
     *   "key_1": [1, 2, 3],
     *   "key_2: "xxxxxxxx"
     * }
     * }</pre>
     * @return 参数列表
     */
    public static Object[] resolveParameters(LinkedHashMap<String, Object> parameters) {
        // 参数需要根据顺序进行赋值
        return parameters.values().stream()
                .flatMap(item-> {
                    Object obj = SQLHelper.resolveParameter(item);
                    if(obj instanceof Collection<?> list){
                        return list.stream();
                    }else {
                        return Stream.of(obj);
                    }
                })
                .toList()
                .toArray(new Object[0]);
    }

    private static Object resolveParameter(Object item){
        if(item instanceof Collection<?> list){
            return list.stream().map(SQLHelper::resolveParameter).toList();
        } else if(item instanceof Map<?,?> map){
            return map.values().stream().flatMap(val->{
                if(val instanceof Collection<?> tmpList){
                    return tmpList.stream();
                }
                return Stream.of(val);
            }).toList();
        } else if(item instanceof Array array){
            return List.of(array);
        }
        return item;
    }

    /**
     * 将参数平铺成一个数组，发布jdbc的参数设置，
     * 例如：select * from table where id = $1 and name = $2
     * 配合 NamedParameterJdbcTemplate 执行即可得到数据结果。
     * @param params 参数列表，必须有序，且key为参数名，value为参数值
     * @return 参数列表
     */
    public static LinkedHashMap<String, Object> resolveNumberParameters(LinkedHashMap<String, Object> params){
        LinkedHashMap<String, Object> parameters = new LinkedHashMap<>();
        if(MapUtil.isNotEmpty(params)) {
            AtomicInteger i = new AtomicInteger(1);
            Arrays.stream(resolveParameters(params)).forEach(item->{
                parameters.put(String.valueOf(i.get()), item);
                i.getAndIncrement();
            });
        }
        return parameters;
    }

    /**
     * 处理SQL查询总数，如果有GROUP BY，说明有聚合查询，需要外层包一层，将查询字段替换成 COUNT(*)。否则直接替换为 COUNT(*)
     * @param originalSql 原始SQL语句
     * @return 将查询字段替换成 COUNT(*) 后返回
     */
    public static String countSqlHandler(final String originalSql){
        Select select = null;
        try {
            log.debug("原始SQL：{}", originalSql);
            // 是否有 行比较 函数 类似：(a, b) > ($a , $b)
            boolean isRowComparison = RowComparisonExpander.isRowComparison(originalSql);
            if(isRowComparison) {
                String lowerSql = RowComparisonExpander.expand(originalSql); // SQL降级处理
                log.debug("降级处理后的SQL：{}", lowerSql);
                select = (Select) CCJSqlParserUtil.parse(lowerSql);
            }else{
                select = (Select) CCJSqlParserUtil.parse(originalSql);
            }
            PlainSelect finalSelect = null; // 最终的 select 语句
            Select changedSelect = null; // 嵌套外壳的内部 select 语句
            if (select instanceof PlainSelect plainSelect) {
                // 创建 COUNT(*) 函数
                Function countFunction = new Function();
                countFunction.setName("COUNT");
                ExpressionList<Column> expressionListCount = new ExpressionList<>(new Column("*")); // 创建参数表达式
                countFunction.setParameters(expressionListCount);

                // 添加 COUNT(*) 函数到 SelectItem 表中
                SelectItem<Function> countItem = new SelectItem<>(countFunction);

                // 判断是否存在GROUP BY
                GroupByElement group = plainSelect.getGroupBy();
                if(group == null) {
                    // 添加到 SelectItem 列表中
                    plainSelect.getSelectItems().clear();
                    plainSelect.getSelectItems().add(countItem);

                    // 移除 ORDER BY 和 LIMIT 子句
                    plainSelect.setOrderByElements(null);
                    plainSelect.setOffset(null);
                    plainSelect.setLimit(null);

//                    return plainSelect.toString();
                    finalSelect = plainSelect;
                    changedSelect = null;
                } else{
                    log.warn("Group already exists, encapsulate original Sql");
                    // 创建子查询（使用ParenthesedSelect替代旧的SubSelect）
                    plainSelect.setOrderByElements(null);// 排序在count查询中无意义。
                    plainSelect.setOffset(null);
                    plainSelect.setLimit(null);
                    ParenthesedSelect subSelect = new ParenthesedSelect();
                    subSelect.setSelect(plainSelect);
                    subSelect.setAlias(new Alias("subquery", false)); // 为子查询添加别名

                    // 创建外层COUNT查询
                    PlainSelect countSelect = new PlainSelect();
                    countSelect.setSelectItems(List.of(countItem));
                    countSelect.setFromItem(subSelect);

//                    return countSelect.toString();
                    finalSelect = countSelect;
                    changedSelect = plainSelect;
                }
                //  如果被降级处理了，需要还原
                if(isRowComparison){
                    RowReverseVisitor visitor = new RowReverseVisitor();
                    Expression whereExpression = finalSelect.getWhere();
                    if(whereExpression == null) {
                        whereExpression = ((PlainSelect)changedSelect).getWhere();
                    }
                    whereExpression.accept(visitor);
                    if (visitor.isMatch()) {
                        String whereStr = whereExpression.toString().replace(visitor.getOrExpression().toString(), visitor.getReplaced().toString());
                        // 剔除多余的括号
//                        String regex = "^\\(\\s*((\\([^()]*\\))\\s*>\\s*(\\([^()]*\\)))\\s*\\)$";
                        String regex = "\\(\\s*((\\([^()]*\\))\\s*>\\s*(\\([^()]*\\)))\\s*\\)";
                        whereStr = whereStr.replaceAll(regex, "$1");
                        if(changedSelect != null){
                            ((PlainSelect) changedSelect).setWhere(CCJSqlParserUtil.parseExpression(whereStr));
                        }else {
                            finalSelect.setWhere(CCJSqlParserUtil.parseExpression(whereStr));
                        }
                        log.debug("还原后SQL：{}", finalSelect);
                    }
                }
                return finalSelect.toString();
            } else {
                log.error("Not a PlainSelect, cannot handle pagination.");
                throw new NormalException(NormalExceptionEnum.REQUEST_INVALIDATE.getCode(), "SQL格式不正确，无法转换查询："+originalSql);
            }
        } catch (JSQLParserException e) {
//            e.printStackTrace();
            log.error(e);
            throw new NormalException(NormalExceptionEnum.OPERATE_FAIL.getCode(), "SQL无法正确解析: "+originalSql);
        }
    }

    /**
     * 重置SQL分页信息
     * @param originalSql 原始SQL语句
     * @param pageNo 页码
     * @param pageSize 每页数量
     * @return 重置后的SQL语句
     */
    public static String resetSqlPageInfo(final String originalSql, long pageNo, long pageSize){
        try {
            Select select = (Select) CCJSqlParserUtil.parse(originalSql);
            if (select instanceof PlainSelect plainSelect) {
                PaginationStrategy strategy = GlobalConfig.getDefaultPaginationStrategy();
                if (strategy != null){
                    plainSelect.setOffset(null);
                    plainSelect.setLimit(null);
                    return strategy.generatePaginationQuery(plainSelect.toString(), pageNo, pageSize);
                }else {

                    Limit limit = plainSelect.getLimit();
                    if (limit == null) {
                        limit = new Limit();
                    }
                    Offset offset = plainSelect.getOffset();
                    if (offset == null) {
                        offset = new Offset();
                    }
                    offset.setOffset(new LongValue(pageNo * pageSize - pageSize));
                    limit.withRowCount(new LongValue(pageSize))
                            .withOffset(offset.getOffset());

                    plainSelect.setOffset(offset);
                    plainSelect.setLimit(limit);

                    return select.toString();
                }
            }else{
                log.error("Not a PlainSelect, cannot handle pagination.");
                throw new NormalException(NormalExceptionEnum.REQUEST_INVALIDATE.getCode(), "SQL格式不正确，无法转换查询："+originalSql);
            }
        } catch (JSQLParserException e) {
            log.error(e);
            throw new NormalException(NormalExceptionEnum.OPERATE_FAIL.getCode(), "SQL无法正确解析: "+originalSql);
        }
    }

    /**
     * 获取SQL分页信息
     * @param originalSql 原始SQL语句
     * @return [0-行数 pageSize, 1-页码 pageNo]
     */
    public static long[] getPageInfo(final String originalSql){
        try {
            Select select = (Select) CCJSqlParserUtil.parse(originalSql);
            if (select instanceof PlainSelect plainSelect) {

                Limit limit = plainSelect.getLimit();
                if (limit == null) {
                    return null;
                }
                long[] limitInfo = new long[2];
                // 获取行数
                if(NumberUtil.isLong(limit.getRowCount().toString())){
                    long rowCount = Long.parseLong(limit.getRowCount().toString());
                    limitInfo[0] = rowCount;
                }
                // 获取偏移量
                if(ArrayUtil.isNotEmpty(limitInfo) && limitInfo[0] > 0 && NumberUtil.isLong(limit.getOffset().toString())){
                    long offset = Long.parseLong(limit.getOffset().toString());
                    // 计算页码
                    limitInfo[1] = (offset / limitInfo[0]) + 1;
                }
                return limitInfo;
            }else{
                log.error("Not a PlainSelect, cannot handle pagination.");
                throw new NormalException(NormalExceptionEnum.REQUEST_INVALIDATE.getCode(), "SQL格式不正确，无法转换查询："+originalSql);
            }
        } catch (JSQLParserException e) {
            log.error(e);
            throw new NormalException(NormalExceptionEnum.OPERATE_FAIL.getCode(), "SQL无法正确解析: "+originalSql);
        }
    }

    /**
     * 假定给的SQL是标准sql，且是 PROPERTY_NAME 类型的语句。
     * @param originalSql
     * @param orderBy
     * @return
     */
    public static String addOrder(final String originalSql, List<ParamSort> orderBy){
        if(CollUtil.isEmpty(orderBy)){
            return originalSql;
        }
        try {
            Select select = (Select) CCJSqlParserUtil.parse(originalSql);
            if (select instanceof PlainSelect plainSelect) {
                List<OrderByElement> orderByList = plainSelect.getOrderByElements();
                if(orderByList == null){
                    orderByList = new ArrayList<>();
                }
                List<OrderByElement> addOrderByList = orderBy.stream().sorted(Comparator.comparingInt(ParamSort::getSno)).map(order->{
                    OrderByElement orderEle = new OrderByElement();
                    orderEle.withExpression(new Column(order.getField()))
                            .withAsc(order.getSortType().equalsIgnoreCase("asc"));
                    return orderEle;
                }).toList();
                orderByList.addAll(addOrderByList);
                plainSelect.setOrderByElements(orderByList);
                return plainSelect.toString();
            }else{
                log.error("Not a PlainSelect, cannot handle pagination.");
                throw new NormalException(NormalExceptionEnum.REQUEST_INVALIDATE.getCode(), "SQL格式不正确，无法转换查询："+originalSql);
            }
        } catch (JSQLParserException e) {
            log.error(e);
            throw new NormalException(NormalExceptionEnum.OPERATE_FAIL.getCode(), "SQL无法正确解析: "+originalSql);
        }
    }


    /**
     * 扫描 SQL，返回所有 **嵌套对象.属性** 的别名，
     * 并判断该属性是否来自“非基础类型”。
     * 示例：b.course_id as "stage.courseId"  → 返回 true（stage 对象内 courseId 非基础）
     */
    public static boolean hasNonBasicNestedProperty(String sql, Class<?> rootClass) {
        Matcher m = ALIAS_DOT_PROP.matcher(sql);
        while (m.find()) {
            String tableAlias = m.group(1);
            String objName    = m.group(2);   // stage
            String propName   = m.group(3);   // courseId

            // 1. 用反射找嵌套属性
            try {
                PropertyDescriptor pd = new PropertyDescriptor(objName, rootClass);
                Method getter = pd.getReadMethod();
                if (getter == null) continue;

                // 2. 拿嵌套对象类型
                Class<?> nestedType = getter.getReturnType();
                // 3. 在该嵌套对象里判断属性是否非基础
                if (isBasicType(nestedType)) continue;   // 整个对象是基础 → 跳过

                // 4. 在该嵌套对象里找属性
//                PropertyDescriptor nestedPd = new PropertyDescriptor(propName, nestedType);
//                return !isBasicType(nestedPd.getPropertyType()); // 属性本身非基础 → 命中
                // 4. 在当前类中判断该属性是否非基础即可
                return !isBasicType(nestedType);
            } catch (IntrospectionException e) {
                // 无此属性 → 继续扫
                continue;
            }
        }
        return false;
    }
}
